import Style from 'ol/style/Style'
import Stroke from 'ol/style/Stroke'
import Fill from 'ol/style/Fill'
import { toContext } from 'ol/render'
import Geojson from 'ol/format/GeoJSON'
import Cesium from '../../Ces/Cesium'
import WeVectorObjectRender from '../../Display/WeVectorObjectRender'
import OLMvt from './OLMvt'

let defaultStyle
const getFeatureStyle = function () {
  if (!defaultStyle) {
    defaultStyle = new Style({
      fill: new Fill({
        color: '#f00',
      }),
      stroke: new Stroke({
        color: '#ff0',
      }),
    })
  }

  return defaultStyle
}

class OlVtImageryRender extends WeVectorObjectRender {
  format: any
  url: string
  defaultStyle: any
  styleFunc: any
  emptyCanvas_: HTMLCanvasElement
  tileIndex: any
  ffx: number
  constructor(options: any = null) {
    options = options??Cesium.Frozen.EMPTY_OBJECT
    super(options)
    this.ffx = options.ffx?? 1
    this.styleFunc = options.styleFunc || getFeatureStyle
    this.url = options.url
    this.tileIndex = options.tileIndex
    this.format = this.tileIndex ? new Geojson() : OLMvt.get()
    this.emptyCanvas_ = document.createElement('canvas')
    this.emptyCanvas_.width = 1
    this.emptyCanvas_.height = 1
    this._readyPromise.resolve(true)
  }

  static scaleCoords(geometry, scaleFactor) {
    if (geometry) {
      const flatCoordinates = geometry.getFlatCoordinates()

      for (let i = 0; i < flatCoordinates.length; ++i) {
        flatCoordinates[i] *= scaleFactor
      }
    }

    return geometry
  }

  rasterizeFeatures = function (
    features,
    level,
    scaleCoord,
    extent_,
    tileWidth,
    tileHeight
  ) {
    const scaleFactor = tileWidth / extent_[2]
    const canvas = document.createElement('canvas')
    const context = canvas.getContext('2d')
    const vectorContext = toContext(context, {
      pixelRatio: 1.25,
      size: [tileWidth, tileHeight],
    })

    let drawCount = 0

    features.forEach((f) => {
      const style = this.styleFunc(f, level)
      if (style) {
        const geometry = f.getGeometry()

        if (scaleCoord) {
          OlVtImageryRender.scaleCoords(geometry, scaleFactor)
        }

        if (Array.isArray(style)) {
          style.forEach((s) => {
            vectorContext.setStyle(s)
            vectorContext.drawGeometry(geometry)
            drawCount++
          })
        } else {
          vectorContext.setStyle(style)
          vectorContext.drawGeometry(geometry)
          drawCount++
        }
      }
    })

    return drawCount > 0 ? canvas : this.emptyCanvas_
  }

  fetchFeatures(url1, x, y, z) {
    if (this.tileIndex) {
      return this.tileIndex.fetchFeatures(x, y, z)
    }
    const url = url1.replace('{z}', z).replace('{y}', y).replace('{x}', x)
    return Cesium.Resource.fetchArrayBuffer(url).then((geojson) => {
      return Promise.resolve(this.format.readFeatures(geojson))
    })
  }

  requestImage(x, y, z, _request, imageryLayerProvider) {
    const { minimumLevel, maximumLevel, tileWidth, tileHeight } =
      imageryLayerProvider

    const emptyCanvas_ = this.emptyCanvas_
    if (z > maximumLevel || z < minimumLevel) {
      return Promise.reject(emptyCanvas_)
    }

    const url = this.url

    const that = this

    const ffx = this.ffx

    const promise = new Promise((resolve) => {
      this.fetchFeatures(url, x, y, z)
        .then(({ features, extent }) => {
          if (features.length === 0) {
            resolve(this.emptyCanvas_)
          } else {
            const resolution = z
            const canvas = that.rasterizeFeatures(
              features.reverse(),
              resolution,
              true,
              extent,
              tileWidth * ffx,
              tileHeight * ffx
            )
            resolve(canvas)
          }
        })
        .catch(() => {
          resolve(this.emptyCanvas_)
        })
    })

    return promise
  }

  isDestroyed() {
    return false
  }
  destroy() {
    return super.destroy()
  }
}

export default OlVtImageryRender
